/**
 * @author Vitor Franco do Carmo (vitor.carmo@capgemini.com)
 * @since 03/04/2013
 *
 */
package br.com.vfcfw.controller.base;

import java.util.Collections;

import javax.annotation.PostConstruct;
import javax.faces.event.ActionEvent;
import javax.faces.model.DataModel;

import br.com.vfcfw.commons.domain.entity.BaseEntity;
import br.com.vfcfw.commons.domain.filter.BaseFilter;
import br.com.vfcfw.commons.util.ConstantesArquitetura;
import br.com.vfcfw.controller.enums.EEstadoCRUD;
import br.com.vfcfw.controller.enums.IEstadoCRUD;
import br.com.vfcfw.controller.util.JSFUtil;

/**
 * Abstração com a implementação padrão dos ManegedBeans (<tt>Faces</tt>) para uma tela do tipo CRUD.
 * 
 * @author Vitor Franco do Carmo (vitor.carmo@capgemini.com)
 * @since 03/04/2013
 * 
 */
public abstract class BaseCRUDFaces<E extends BaseEntity, F extends BaseFilter<E>, B extends E> extends
    BaseFaces<E, F, B> implements CRUDFaces<E> {

    /** */
    private static final long serialVersionUID = -7733765553553892929L;

    /** Estado do Managed Bean. */
    protected IEstadoCRUD state = EEstadoCRUD.PESQUISANDO;

    /**
     * Construtor da classe.
     */
    public BaseCRUDFaces() {

        super();
        setEstadoPesquisa();
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#pesquisar()
     */
    @Override
    public void pesquisar() {

        setEstadoPesquisa();

        if (!isPaginacaoPorDemanda()) {

            this.listResultadoPesquisa = Collections.emptyList();

            if (isPesquisaPorCriteria()) {

                this.listResultadoPesquisa = entityService.consultarPorCriterio(getFiltroPesquisa());
            } else {

                this.listResultadoPesquisa = entityService.consultarPorFiltro(getFiltroPesquisa());
            }
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#preIncluir(javax.faces.event.ActionEvent)
     */
    @Override
    public void preIncluir(ActionEvent event) {

        this.entity = obterNovaInstanciaEntity();

        setEstadoInsercao();
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#incluir()
     */
    @Override
    public void incluir() {

        this.entityService.incluir(getEntity());

        limpar(null);

        exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_INFO_OPERACAO_REALIZADA_COM_SUCESSO));

        pesquisar();
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#preAlterar(javax.faces.event.ActionEvent)
     */
    @Override
    public void preAlterar(ActionEvent event) {

        E entityAlterar = getMarcadoParaEdicao(event);

        preAlterar(entityAlterar);
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#preAlterar(com.capgemini.domain.base.BaseEntity)
     */
    @Override
    public void preAlterar(E entityAlterar) {

        this.entity = entityAlterar;

        E entityPreAlterar = entityService.consultarPorId(entity); // evita LazyInitializationException

        if (!possuiValor(entityPreAlterar)) {

            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_ERRO_REGISTRO_NAO_EXISTE));
        } else {

            this.entity = entityPreAlterar;

            setEstadoEdicao();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#alterar()
     */
    @Override
    public void alterar() {

        E entityPreAlterar = entityService.consultarPorId(getEntity());

        if (!possuiValor(entityPreAlterar)) {
            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_ERRO_REGISTRO_NAO_EXISTE));
        } else {
            this.entityService.alterar(getEntity());
            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_INFO_OPERACAO_REALIZADA_COM_SUCESSO));
        }

        limpar(null);
        pesquisar();
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#visualizar(javax.faces.event.ActionEvent)
     */
    @Override
    public void visualizar(ActionEvent event) {

        E entityVisualizar = getMarcadoParaVisualizar(event);

        visualizar(entityVisualizar);
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#visualizar(com.capgemini.domain.base.BaseEntity)
     */
    @Override
    public void visualizar(E entityVisualizar) {

        this.entity = entityVisualizar;

        E entityPreVisualizar = entityService.consultarPorId(entity);

        if (!possuiValor(entityPreVisualizar)) {

            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_ERRO_REGISTRO_NAO_EXISTE));
        } else {

            this.entity = entityPreVisualizar;

            setEstadoVisualizacao();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#salvar()
     */
    @Override
    public void salvar() {

        if (isInserindo()) {

            incluir();
        } else {

            alterar();
        }
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.BaseFaces#inicializar()
     */
    @Override
    @PostConstruct
    public void inicializar() {

    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.BaseFaces#getDataModel()
     */
    @Override
    public DataModel<B> getDataModel() {

        return null;
    }

    /**
     * Verifica se o PrimefacesDataModel possui valor.
     * 
     * @return <tt>TRUE</tt> se possuir valor, <tt>FALSE</tt> caso contrário.
     */
    public boolean possuiRegistrosDataModel() {

        return false;
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#excluir()
     */
    @Override
    public String excluir() {

        if (possuiValor(getEntitySelecionado())) {

            excluir(getEntitySelecionado());
        }
        return null;
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#excluir(javax.faces.event.ActionEvent)
     */
    @Override
    public void excluir(ActionEvent event) {

        E entityExclusao = getMarcadoParaExclusao(event);

        excluir(entityExclusao);
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#excluir(com.capgemini.domain.base.BaseEntity)
     */
    @Override
    public void excluir(E entityExclusao) {

        E entityPreExcluir = entityService.consultarPorId(entityExclusao);

        if (!possuiValor(entityPreExcluir)) {

            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_ERRO_REGISTRO_NAO_EXISTE));
        } else {

            entityService.excluir(entityExclusao);

            exibirMensagemInformacao(getMensagem(ConstantesArquitetura.MSG_INFO_OPERACAO_REALIZADA_COM_SUCESSO));
        }

        pesquisar();
    }

    /**
     * {@inheritDoc}
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#cancelar(javax.faces.event.ActionEvent)
     */
    @Override
    public void cancelar(ActionEvent event) {

        limpar(event);

        setEstadoPesquisa();
    }

    /**
     * {@inheritDoc} <p>Esta implementação já esta instanciando a entidade principal do Managed bean, o filtro de
     * pesquisa, a lista de resultado de pesquisa e os valores submetidos dos campos da página.</p>
     * 
     * @see br.com.vfcfw.controller.base.CRUDFaces#limpar(javax.faces.event.ActionEvent)
     */
    @Override
    public void limpar(ActionEvent event) {

        this.entity = obterNovaInstanciaEntity();
        this.filtroPesquisa = obterNovaInstanciaFilter();
        this.listResultadoPesquisa = Collections.emptyList();
        JSFUtil.limparValoresSubmetidos(getForm());
    }

    /**
     * Verifica se o Managed Bean está no estado de pesquisa.
     * 
     * @return <tt>TRUE</tt> se estiver no estado de pesquisa, <tt>FALSE</tt> caso contrário.
     */
    public boolean isPesquisando() {

        return getState().isPesquisando(this.state);
    }

    /**
     * Verifica se o Managed Bean está no estado de inserção.
     * 
     * @return <tt>TRUE</tt> se estiver no estado de inserção, <tt>FALSE</tt> caso contrário.
     */
    public boolean isInserindo() {

        return getState().isInserindo(this.state);
    }

    /**
     * Verifica se o Managed Bean está no estado de edição.
     * 
     * @return <tt>TRUE</tt> se estiver no estado de edição, <tt>FALSE</tt> caso contrário.
     */
    public boolean isEditando() {

        return getState().isEditando(this.state);
    }

    /**
     * Verifica se o Managed Bean está no estado de visualização.
     * 
     * @return <tt>TRUE</tt> se estiver no estado de visualização, <tt>FALSE</tt> caso contrário.
     */
    public boolean isVisualizando() {

        return getState().isVisualizando(this.state);
    }

    /**
     * Especifica o estado do Managed Bean como de pesquisa.
     */
    public void setEstadoPesquisa() {

        setState(EEstadoCRUD.PESQUISANDO);
    }

    /**
     * Especifica o estado do Managed Bean como de inserção.
     */
    public void setEstadoInsercao() {

        setState(EEstadoCRUD.INSERINDO);
    }

    /**
     * Especifica o estado do Managed Bean como de edição.
     */
    public void setEstadoEdicao() {

        setState(EEstadoCRUD.EDITANDO);
    }

    /**
     * Especifica o estado do Managed Bean como de visualização.
     */
    public void setEstadoVisualizacao() {

        setState(EEstadoCRUD.VISUALIZANDO);
    }

    /**
     * Retorna o valor do atributo state.
     * 
     * @return Valor do atributo state.
     */
    public IEstadoCRUD getState() {

        return state;
    }

    /**
     * Especifica o valor do atributo state.
     * 
     * @param state IEstadoCRUD do state a ser setado.
     */
    public void setState(IEstadoCRUD state) {

        this.state = state;
    }

    /**
     * Retorna o objeto (<tt>contido no atributo <b>editar</b></tt>) marcado para edição na GRID.
     * 
     * @param event {@link ActionEvent}.
     * @return Objeto marcado para edição.
     */
    @SuppressWarnings("unchecked")
    protected E getMarcadoParaEdicao(ActionEvent event) {

        return (E) event.getComponent().getAttributes().get("editar");
    }

    /**
     * Retorna o objeto (<tt>contido no atributo <b>visualizar</b></tt>) marcado para visualização na GRID.
     * 
     * @param event {@link ActionEvent}.
     * @return Objeto marcado para visualização.
     */
    @SuppressWarnings("unchecked")
    protected E getMarcadoParaVisualizar(ActionEvent event) {

        return (E) event.getComponent().getAttributes().get("visualizar");
    }

    /**
     * Retorna o objeto (<tt>contido no atributo <b>excluir</b></tt>) marcado para exclusão na GRID.
     * 
     * @param event {@link ActionEvent}.
     * @return Objeto marcado para exclusão.
     */
    @SuppressWarnings("unchecked")
    protected E getMarcadoParaExclusao(ActionEvent event) {

        return (E) event.getComponent().getAttributes().get("excluir");
    }

}
